﻿using System;
using System.Collections.Generic;

using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using Estimate.Common.DBFieldTypes;
using Estimate.Common.Repository;
using Estimate.Common.Entity;

namespace Estimate.Common.DataImporter
{
    /// <summary>
    /// 指导站点数据导入
    /// </summary>
    public class SiteForeImporter : IDataImporter
    {
        /// <summary>
        /// 存储 站点;值 数组。其中键值是 span和factor合并成的字符串
        /// </summary>
        protected Dictionary<string, List<string>> processedData;

        protected float[] LevList = { 20.0f, 18.0f, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 13.0f, 14.0f, 21.0f, 26.0f, 8.0f, 15.0f, 22.0f, 27.0f, 9.0f, 16.0f, 23.0f, 28.0f, 10.0f, 17.0f, 24.0f, 11.0f, 25.0f, 12.0f };

        public int GetLevIndex(float lev)
        {
            for (int i = 0; i < LevList.Length - 1; i++)
            {
                if (LevList[i] == lev)
                {
                    return i;
                }
            }

            return 0;
        }

        public float GetMax(float one, float other)
        {
            if (one == other)
                return one;
            else if (GetLevIndex(one) > GetLevIndex(other))
            {
                return one;
            }
            else
            {
                return other;
            }
        }

        /// <summary>
        /// 数据库访问类
        /// </summary>
        protected ForeRepository siteForeRepository;

        protected List<string> stations;

        /// <summary>
        /// 注册一个指导站数据导入类
        /// </summary>
        /// <param name="repository">数据库访问类</param>
        /// <param name="compare">指导站点入库与出库时，盐城和镇江映射的站号不一样。在入库时，即将站号进行转变</param>
        public SiteForeImporter(ForeRepository repository,string[] station)
        {
            siteForeRepository = repository;
            processedData = new Dictionary<string, List<string>>();
            stations = new List<string>(station);
        }

        /// <summary>
        /// 由文件名获取发报时刻
        /// </summary>
        public ForeClockType getClockByFileName(string filename)
        {
            string strClock= filename.Substring(6,2);
            if(strClock == "08")
                return ForeClockType.H08;
            else
                return ForeClockType.H20;
        }

        public DateTime getDateByFileFirstLine(string line)
        {
            int index = line.IndexOf("年");
            int year = 2000 + Convert.ToInt32(line.Substring(index-2,2));
            int month = Convert.ToInt32(line.Substring(index+1,2));
            int day = Convert.ToInt32(line.Substring(index+4,2));
            return new DateTime(year,month,day);
        }

        public bool  IsStationExist(string st)
        {
            return stations.Contains(st);
        }

        /// <summary>
        /// 根据给定的参数，判断出ForeSpanType的值类型
        /// </summary>
        /// <param name="hours">小时数</param>
        /// <param name="is12Hours">是否是12小时制</param>
        /// <param name="isFirst">是否是前半截</param>
        /// <returns>ForeSpanType的值</returns>
        private ForeSpanType getForespanFromString(string hours, bool is12Hours, bool isFirst)
        {
            switch (hours)
            {               
                case "024":
                    if (is12Hours)
                    {
                        if(isFirst)
                            return ForeSpanType.FD00_12;
                        else
                            return ForeSpanType.FD12_24;
                    }
                    else
                        return ForeSpanType.FD00_24;
                case "048":
                    if (is12Hours)
                    {
                        if(isFirst)
                            return ForeSpanType.FD24_36;
                        else
                            return ForeSpanType.FD36_48;
                    }
                    else
                        return ForeSpanType.FD24_48;
                case "072":
                    if (is12Hours)
                    {
                        if(isFirst)
                            return ForeSpanType.FD48_60;
                        else
                            return ForeSpanType.FD60_72;
                    }
                    else
                        return ForeSpanType.FD48_72;
                case "096":
                    if (is12Hours)
                    {
                        if(isFirst)
                            return ForeSpanType.FD72_84;
                        else
                            return ForeSpanType.FD84_96;
                    }
                    else
                        return ForeSpanType.FD72_96;
                case "120":
                    if (is12Hours)
                        {
                        if(isFirst)
                            return ForeSpanType.FD96_108;
                        else
                            return ForeSpanType.FD108_120;
                    }
                    else
                        return ForeSpanType.FD96_120;
                case "144":
                    if (is12Hours)
                    {
                        if (isFirst)
                            return ForeSpanType.FD120_132;
                        else
                            return ForeSpanType.FD132_144;
                    }
                    else
                        return ForeSpanType.FD120_144;
                case "168":
                    if (is12Hours)
                    {
                        if(isFirst)
                            return ForeSpanType.FD144_156;
                        else
                            return ForeSpanType.FD156_168;
                    }
                    else
                        return ForeSpanType.FD144_168;
                default:
                    return ForeSpanType.FD00_12;
            }
        }

        private void clearProcessedData()
        {
            foreach (List<string> value in processedData.Values)
            {
                value.Clear();
            }
        }

        /// <summary>
        /// 向预处理的对象中添加一个站点及预测值
        /// </summary>
        /// <param name="span">预测时效</param>
        /// <param name="type">要素</param>
        /// <param name="value">预测站点及值</param>
        private void addData(ForeSpanType span, ForeFactorType type, string value)
        {
            string key = string.Format("{0}-{1}", span.ToString(), type.ToString());
            if (!processedData.ContainsKey(key))
                processedData[key] = new List<string>();
            processedData[key].Add(value);
        }

        /// <summary>
        /// 将缓存的数据全部存入数据库
        /// </summary>
        /// <param name="date">日期</param>
        /// <param name="clock">起报时间</param>
        /// <param name="sendpoint">发报时间</param>
        private void saveDataToDataBase(DateTime date, ForeClockType clock, ForeSendpointType sendpoint)
        {
            ForeEntity entity = new ForeEntity();
            entity.Date = date;
            entity.Clock = clock;
            entity.Sendpoint = sendpoint;
            entity.Semitype = ForeSemiType.Auto;

            string pattern = @"-";
            foreach (KeyValuePair<string, List<string>> pair in processedData)
            {
                //去除没有数据的预报
                if (pair.Value.Count == 0)
                    continue;

                string[] keys = Regex.Split(pair.Key, pattern, RegexOptions.IgnoreCase);
                ForeSpanType span = (ForeSpanType)Enum.Parse(typeof(ForeSpanType), keys[0]);
                ForeFactorType type = (ForeFactorType)Enum.Parse(typeof(ForeFactorType), keys[1]);

                entity.Span = span;
                entity.Type = type;
                entity.Value = string.Join("|", pair.Value.ToArray());
                entity.Semi = "";
                if (DataWillBeSaved(entity))
                {
                    siteForeRepository.SetForeRecord(entity);
                }
            }            
        }

        public void ImportDataFromFile(string filepath)
        {
            StreamReader file = new StreamReader(filepath, Encoding.Default);

            string line = file.ReadLine();
            //根据第一行内容，查出生成报告的日期
            DateTime date = getDateByFileFirstLine(line);
            ImportDataFromFileWithDate(filepath, date);
        }


        public void ImportDataFromFileWithDate(string filepath, DateTime dt)
        {
            clearProcessedData();


            //根据文件名获取clock和span参数
            string filename = Path.GetFileName(filepath);
            ForeClockType clock = getClockByFileName(filename);
            ForeSendpointType sendpoint = clock == ForeClockType.H08 ? ForeSendpointType.H06 : ForeSendpointType.H16;
            string strSpan = filename.Substring(filename.IndexOf('.') + 1);

            //021 和 012 文件是从024文件中分拆出来的。不予处理
            if (strSpan == "012" || strSpan == "021")
                return;

            string st;
            float wth, preWth,maxt, mint, wd, preWd, ws, preWs;
            ForeSpanType span;

            StreamReader file = new StreamReader(filepath, Encoding.Default);

            string line = file.ReadLine();

            line = file.ReadLine();

            string pattern = @"\s*[\t|\ ]+\s*";
            while (!file.EndOfStream)
            {
                line = file.ReadLine();
                string[] foreserial = Regex.Split(line, pattern, RegexOptions.IgnoreCase);

                List<string> noEmptyForeserial = new List<string>();
                foreach (string s in foreserial)
                {
                    if(s.Length > 0)
                        noEmptyForeserial.Add(s);
                }
                if (noEmptyForeserial.Count != 12)
                    continue;
                foreserial = noEmptyForeserial.ToArray();

                st = foreserial[0];
                if(! IsStationExist(st))
                    continue;
                

                span = getForespanFromString(strSpan, true, true);
                preWth = wth = Convert.ToSingle(foreserial[4]);
                preWd = wd = Convert.ToSingle(foreserial[5]);
                preWs = ws = Convert.ToSingle(foreserial[6]);

                addData(span, ForeFactorType.wth, string.Format("{0};{1}", st, wth.ToString()));
                addData(span, ForeFactorType.wd, string.Format("{0};{1}", st, wd.ToString()));
                addData(span, ForeFactorType.ws, string.Format("{0};{1}", st, ws.ToString()));

                span = getForespanFromString(strSpan, true, false);
                wth = Convert.ToSingle(foreserial[9]);
                wd = Convert.ToSingle(foreserial[10]);
                ws = Convert.ToSingle(foreserial[11]);

                addData(span, ForeFactorType.wth, string.Format("{0};{1}", st, wth.ToString()));
                addData(span, ForeFactorType.wd, string.Format("{0};{1}", st, wd.ToString()));
                addData(span, ForeFactorType.ws, string.Format("{0};{1}", st, ws.ToString()));

                span = getForespanFromString(strSpan, false, false);
                wth = GetMax(wth, preWth);
                wd = Math.Max(wd, preWd);
                ws = Math.Max(ws, preWs);
                mint = Convert.ToSingle(foreserial[7]);
                maxt = Convert.ToSingle(foreserial[8]);

                addData(span, ForeFactorType.wth, string.Format("{0};{1}", st, wth.ToString()));
                addData(span, ForeFactorType.wd, string.Format("{0};{1}", st, wd.ToString()));
                addData(span, ForeFactorType.ws, string.Format("{0};{1}", st, ws.ToString()));
                addData(span, ForeFactorType.mint, string.Format("{0};{1}", st, mint.ToString()));
                addData(span, ForeFactorType.maxt, string.Format("{0};{1}", st, maxt.ToString()));


            }
            saveDataToDataBase(dt, clock, sendpoint);
        }


        /// <summary>
        /// 未实现该事件。目前用不到
        /// </summary>
        public event EventHandler AfterDataInDatabase;



        public event EventHandler BeforeDataInDatabase;

        /// <summary>
        /// 在保存数据前引发 BeforeDataInDatabase 信号
        /// 并将参数中是否入库的变量返回
        /// </summary>
        /// <param name="score">即将入库的数据</param>
        /// <returns>是否入库。 true则表示将入库。 false则表示不会入库</returns>
        public bool DataWillBeSaved(ForeEntity entity)
        {
            object data = entity.Clone();
            if (BeforeDataInDatabase != null)
            {
                FindDataEventArgs args = new FindDataEventArgs(data);
                BeforeDataInDatabase(this, args);
                return args.SaveData;
            }
            return true;
        }
    }
}
